import java.util.*;
import java.util.Map.Entry;

public class CollocationFeature {
  private final int featLength_;
  private TreeMap<String, LinkedList<LinkedList<String>>> senseFeatMap_;
  private WordSenseCount wsc_;
  

  public CollocationFeature(int length, WordSenseCount wsc) {
    featLength_ = length;
    senseFeatMap_ = new TreeMap<String, LinkedList<LinkedList<String>>>();
    wsc_ = wsc;
  }
  
  public void handleInstance(XMLParserState state) {
    Vector<String> senses = state.getSensesForInstance();
    for (int i = 0; i < senses.size(); ++i) {
      insertFeature(state.getLexelt(), senses.get(i), state.getPrevNList(), state.getSuccNList());
    }    
  }
  
  public static void handleCharacters(final String str, XMLParserState state) {
    String line = str.replaceAll(WSD.regexSeparator, " ");
    String[] words = line.trim().split(" |[.]|\\n");
    for (String text : words) {
      if (text.equals(" ") || text.equals("") || text == null)
        continue;
      state.addCollocationWord(text.toLowerCase());
    }
  }  

  public void insertFeature(
      final String targetWord, final String senseid,
      final LinkedList<String> prevNwords, final LinkedList<String> succNwords) {
    if (prevNwords.size() != featLength_ || succNwords.size() != featLength_) {
      System.err.println("Adding dummy feature during training");
      while (succNwords.size() != featLength_) {
        if (succNwords.size() > featLength_)
          succNwords.remove();
        else
          succNwords.add("dummy");
      }
    }
    LinkedList<String> feature = (LinkedList<String>) prevNwords.clone();
    feature.addAll(succNwords);

    LinkedList<LinkedList<String>> list = senseFeatMap_.get(senseid);
    if (list == null) {
      list = new LinkedList<LinkedList<String>>();
    }
    list.add(feature);
    senseFeatMap_.put(senseid, list);

//     System.out.println(feature + " : " + targetWord + " : " + senseid);
  }

  public TreeMap<String, LinkedList<LinkedList<String>>> getSenseFeatMap() {
    return senseFeatMap_;
  }
  
  public void printStats() {
    System.out.println(getSenseFeatMap());
  }

  private double probSenseGivenWord(String targetWord, String sense) {
    int countWordSense = wsc_.getSenseCountForWord(targetWord, sense);
    int countWord = wsc_.getWordCount(targetWord);
    if (countWordSense == 0 || countWord == 0) {
      if (countWord == 0)
        System.err.println(" Handling req, countWord is 0 HANDLE UNKNOWN WORDS");
      if (WSD.doCollSmoothing) {
        return (WSD.lambda / ((double) countWord));
      } else {
        return 0.00000001;
      }
    }
    double prob = ((double) countWordSense) / ((double) countWord);
    return prob;
  }

  private double probFeatGivenSense_NB(String feat, String sense, int position) {
    int countFeatSense = 0;
    int countSense = 0;
    LinkedList<LinkedList<String>> list = senseFeatMap_.get(sense);
    if (list == null) {
      // System.err.println(" HANDLE Unseen SENSE while testing");
    } else {
      for (LinkedList<String> featVector : list) {
        countSense++;
        if (featVector.get(position).equals(feat)) {
          countFeatSense++;
        }
      }
    }
    if (countFeatSense == 0 || countSense == 0) {
      if (countSense == 0){
//        System.err.println(" count Sense is 0 HANDLE UNKNOWN WORDS");	      
        return  0.00000001;
      }
      double val = 0;
      if (WSD.doCollSmoothing) {
        val =  (WSD.lambda / ((double) countSense));
      } else {
        val = 0.00000001;
      }
      return val;
    }
    double prob = 0;
    if(WSD.doCollSmoothing){
      prob = ((double) countFeatSense + WSD.lambda) / ((double) countSense*(1+WSD.lambda));
    } else {
      prob = ((double) countFeatSense) / ((double) countSense);
    }
    return prob;
  }

  private String predictSenseForFeature_NB(LinkedList<String> feature,
      String targetWord, WSD wsdObj) {
    Set<String> senses = wsdObj.senseInv_.givePossibleSenses(targetWord);
    HashMap<String, Double> probSense = new HashMap<String, Double>();
    for (String sense : senses) {
      double P_s = Math.log10(probSenseGivenWord(targetWord, sense));
      double P_fi_s = 0;
      for (int i = 0; i < 2 * featLength_; i++) {
        double tmp = Math.log10(probFeatGivenSense_NB(feature.get(i), sense, i));
        if (tmp == Double.NEGATIVE_INFINITY)
          System.out.println("Kuch garbar hai");
        P_fi_s = P_fi_s + tmp;
      }
      probSense.put(sense, P_s + P_fi_s);
    }

    // find sense w/ max prob and return that sense
    double maxProb = Double.NEGATIVE_INFINITY;
    String maxSense = null;
    for (Entry<String, Double> entry : probSense.entrySet()) {
      if (entry.getValue() > maxProb) {
        maxProb = entry.getValue();
        maxSense = entry.getKey();
      }
    }
    if (maxSense == null)
      System.out.println("kuch garbar hai");
    ; // breakpoint to debug
    return maxSense;
  }

  public String getFeatSense(String targetWord, LinkedList<String> prevNwords,
      LinkedList<String> succNwords, WSD wsdObj) {
    if (prevNwords.size() != featLength_ || succNwords.size() != featLength_) {
      System.err.println("Adding dummy feature while Testing");
      while (succNwords.size() != featLength_) {
        if (succNwords.size() > featLength_)
          succNwords.remove();
        else
          succNwords.add("dummy");
      }
    }
    LinkedList<String> feature = (LinkedList<String>) prevNwords.clone();
    feature.addAll(succNwords);
    String prediction = predictSenseForFeature_NB(feature, targetWord, wsdObj);
    return prediction;
  }
  
  private double countFeatGivenSense_CM(String feat, String sense, int position) {
    int countFeatSense = 0;
    LinkedList<LinkedList<String>> list = senseFeatMap_.get(sense);
    if (list != null) {
      for (LinkedList<String> featVector : list) {
        if (featVector.get(position).equals(feat)) {
          countFeatSense++;
        }
      }
    }
    return countFeatSense;
  }
  
  private String mostFreqSenseGivenWord (String targetWord){
    return wsc_.getMostFrequentSense(targetWord);
    
    /*
    Set<String> senses = WSD.senseInv_.givePossibleSenses(targetWord);
    int i = 0;
    String maxSense = null;
    Double maxProb = Double.NEGATIVE_INFINITY;
    for (String sense : senses) {
      if (i == 0) {
        maxSense = sense;
        i++;
      }
      Double prob = probSenseGivenWord(targetWord, sense);
      if (prob > maxProb) {
        maxProb = prob;
        maxSense = sense;
      }
    }
    return maxSense;
    */
  }
  
  private String predictSenseForFeature_CM(LinkedList<String> feature,
      String targetWord, WSD wsdObject) {
    Set<String> senses = wsdObject.senseInv_.givePossibleSenses(targetWord);
    HashMap<String, Double> probSense = new HashMap<String, Double>();
    for (String sense : senses) {
      double countFeatVec =0;
      double normCountFeatVec = 0;
      for (int i = 0; i < 2 * featLength_; i++) {
        double tmp = countFeatGivenSense_CM(feature.get(i), sense, i);
        countFeatVec += tmp;
        normCountFeatVec += tmp*tmp;
      }
      Double prob = probSenseGivenWord(targetWord, sense);
      if (normCountFeatVec != 0) {
        normCountFeatVec = Math.sqrt(normCountFeatVec);
        Double tmp = prob*(countFeatVec/normCountFeatVec);
        probSense.put(sense, tmp);
      } else { 
        probSense.put(sense,prob);
      }
    }

    // find sense w/ max prob and return that sense
    double maxProb = Double.NEGATIVE_INFINITY;
    String maxSense = null;
    for (Entry<String, Double> entry : probSense.entrySet()) {
      if (entry.getValue() > maxProb) {
        maxProb = entry.getValue();
        maxSense = entry.getKey();
      }
    }
    if (maxSense == null)
      System.err.println("kuch garbar hai");
    if(maxProb ==0 ) {// means that all cosines are 0
      maxSense = mostFreqSenseGivenWord (targetWord);
    }
    return maxSense;
  }
  
  public String getSenseGivenCollFeature_CM(String targetWord, LinkedList<String> prevNwords,
      LinkedList<String> succNwords, WSD wsdObject) {
    if (prevNwords.size() != featLength_ || succNwords.size() != featLength_) {
      System.err.println("Adding dummy feature while Testing");
      while (succNwords.size() != featLength_) {
        if (succNwords.size() > featLength_)
          succNwords.remove();
        else
          succNwords.add("dummy2");
      }
    }
    LinkedList<String> feature = (LinkedList<String>) prevNwords.clone();
    feature.addAll(succNwords);
    String prediction = predictSenseForFeature_CM(feature, targetWord, wsdObject);
    return prediction;
  }

}
